/**
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.brixcms.markup;

import org.apache.wicket.Component;
import org.apache.wicket.MarkupContainer;
import org.brixcms.jcr.wrapper.BrixNode;
import org.brixcms.markup.tag.ComponentTag;
import org.brixcms.markup.tag.Tag;
import org.brixcms.plugin.site.SitePlugin;
import org.brixcms.web.generic.IGenericComponent;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * Helper class for components with custom node markup. This class takes care of generating the markup and
 * creating/removing of appropriate wicket components.
 *
 * @author Matej Knopp
 */
public class MarkupHelper implements Serializable {
    /**
     * Each tag component ids prefixed by this.
     */
    private final static String COMPONENT_PREFIX = "brix-";

    private final IGenericComponent<BrixNode> component;

    private String markup = null;

    public MarkupHelper(IGenericComponent<BrixNode> component) {
        this.component = component;
        initMarkup();
    }

    /**
     * Generates the markup and makes sure that the markup container contains all components generated by the {@link
     * ComponentTag}s in markup. Also removes components no longer present in markup.
     */
    private void initMarkup() {
        final Set<String> components = new HashSet<String>();
        GeneratedMarkup markup = getMarkupCache().getMarkup(component);

        MarkupRenderer renderer = new MarkupRenderer(markup.items, markup.doctype) {
            @Override
            void postprocessTagAttributes(Tag tag, Map<String, String> attributes) {
                // if during rendering we have a ComponentTag
                if (tag instanceof ComponentTag && tag.getType() != Tag.Type.CLOSE) {
                    ComponentTag componentTag = (ComponentTag) tag;
                    String id = getComponentID(componentTag);

                    // check if the component already is in hierarchy
                    if (getExistingComponents().contains(id)) {
                        // just put the wicket:id attribute to component tag
                        attributes.put("wicket:id", id);
                        components.add(id);
                    } else {
                        // otherwise we need to create the component instance
                        Component c = componentTag.getComponent(id, component.getModel());
                        if (c != null) {
                            attributes.put("wicket:id", id);
                            components.add(id);
                            ((MarkupContainer) component).add(c);
                        }
                    }
                }
            }
        };
        this.markup = renderer.render();

        // go through existing components and remove those not present in
        // current markup
        for (String s : getExistingComponents()) {
            if (!components.contains(s)) {
                ((MarkupContainer) component).get(s).remove();
            }
        }
    }

    /**
     * Returns existing tag components on page.
     *
     * @return
     */
    private Set<String> getExistingComponents() {
        Set<String> result = new HashSet<String>();
        Iterator<? extends Component> i = ((MarkupContainer) component).iterator();
        while (i.hasNext()) {
            Component c = i.next();
            if (c.getId().startsWith(COMPONENT_PREFIX)) {
                result.add(c.getId());
            }
        }
        return result;
    }

    public static String getComponentID(ComponentTag tag) {
        String uid = tag.getUniqueTagId();
        return COMPONENT_PREFIX + (uid != null ? uid.toString() : "");
    }

    public String getMarkup() {
        return markup;
    }

    private MarkupCache getMarkupCache() {
        return SitePlugin.get().getMarkupCache();
    }
}
